// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===--------- Reactor.h - R virtual machine description --------------===//
//
//                          The VMKit project
//
// This file is distributed under the Purdue University Open Source
// License. See LICENSE.TXT for details.
//
//===------------------------------------------------------------------===//

//===------------------ Header Includes ------------------===//

#include "version.h"
#include "Reactor.h"
#include "RModule.h"
#include "Environment.h"
#include "Evaluator.h"
#include "RCompiler.h"
#include "RJITCompiler.h"
#include "RAttributes.h"
#include <sys/time.h>

//===------------------ End Header Includes -------------===//

using namespace R;

void ClArgumentsInfo::readArgs(Reactor *vm) {
  int dashdash = 0;
  fname = NULL;
	verbose = 0;
	debug = 0;
	jitc = 0;
	precompile = 0;
	chronometer = 0;
	skipBaseLibrary = 0;

  for(int i = 1; i < argc; i++){
    char *current = argv[i];
    if(current[0] == '-' && !dashdash){
      if(!strcmp(current, "-v") || !strcmp(current, "--verbose"))
        verbose ++;
      else if(!strcmp(current, "-d") || !strcmp(current, "--debugger"))
				debug = 1;
      else if(!strcmp(current, "-jit") || !strcmp(current, "--jit-compile"))
      	jitc = 1;
      else if(!strcmp(current, "-aot") || !strcmp(current, "--pre-compile"))
      	precompile = 1;
      else if(!strcmp(current, "-time"))
				chronometer = 1;
      else if(!strcmp(current, "--skip-base"))
				skipBaseLibrary = 1;
      else if(!strcmp(current, "--"))
        dashdash ++;
      else if(!strcmp(current, "--version")){
				printVersion();
				exit(0);
			} else {
        warning("Unknown option: %s\n", current);
      }
    } else {
      if(fname != NULL){
        fprintf(stderr,
            "More than one filename given: '%s', '%s'\n", fname, current);
      } else {
        fname = current;
      }
    }
  }
  if(!fname){
		printInformation();
		rfatal("No filename given !\n");
	}
}

void ClArgumentsInfo::printInformation() {
  fprintf(stderr, "Syntax: reactor [ -v|--verbose ] [ -d|--debbuger ] [ -jit|--jit-compile ] [ --skip-base ] [--] filename\n");
}

void ClArgumentsInfo::printVersion() {
  fprintf(stdout, "Reactor for R lang version '%s'\n", REACTOR_VERSION);
}

// this variable does not exist in the vmkit svn, it is defined in my version to debug the GC (Gaël)
//extern "C" int debugScanning;

Reactor::Reactor(vmkit::BumpPtrAllocator& Alloc, RCompiler* compiler, vmkit::CompiledFrames** frames) :
	VirtualMachine(Alloc, frames) {
	this->compiler = compiler;
	compiler->vm = this;
	//debugScanning = 1;
}

Reactor::~Reactor() {
}

void Reactor::startCollection() {
  if(argumentsInfo.verbose) printf("[reactor info]: Starting a collection\n");
}

void Reactor::endCollection() {
  if(argumentsInfo.verbose) printf("[reactor info]: Collection finished\n");
}

void Reactor::scanWeakReferencesQueue(word_t closure) {
  nyi();
}

void Reactor::scanSoftReferencesQueue(word_t closure) {
  nyi();
}

void Reactor::scanPhantomReferencesQueue(word_t closure) {
  nyi();
}

void Reactor::scanFinalizationQueue(word_t closure) {
  nyi();
}

void Reactor::addFinalizationCandidate(gc* obj) {
  // printf("[Reactor.cpp]: adding obj @%p as finalization candidate\n", obj);
  // finalizerThread->addFinalizationCandidate(object);
}

size_t Reactor::getObjectSize(gc* obj) {
  nyi();
  // TODO cast obj into Any, get class from obj
  // and then do a switch case to match type.
  // return sizeof(Any_type)
  return 0;
}

const char* Reactor::getObjectTypeName(gc* obj) {
  nyi();
  // TODO cast obj into Any, get class from obj
  // and then do a switch case to match type.
  // return Type::name (have to create member)
  return (char*)NULL;
}

void Reactor::printMethod(vmkit::FrameInfo* FI, word_t ip, word_t addr) {
	if (FI->Metadata == NULL) {
		vmkit::MethodInfoHelper::print(ip, addr);
		return;
	}
	FunInfo* fun = static_cast<FunInfo*>(FI->Metadata);
	fprintf(stderr, "; %p (%p) in %s\t<%s("WORD")::%d>\t0x%x to 0x%x\tclosures: %d\tJIT: %p\tcode: %p\n",
			(void*)ip,
			(void*)addr,
			fun->jitFun->getName().data(),
			fun->module->fileName,
			fun->module->moduleId,
			fun->id,
			fun->start,
			fun->end,
			fun->hasClosure,
			fun->jitFun,
			fun->code);
}

void Reactor::nullPointerException() {
  nyi();
}

void Reactor::stackOverflowError() {
  nyi();
}

void Reactor::runApplication(int argc, char** argv) {
  argumentsInfo.argc = argc;
  argumentsInfo.argv = argv;

  mainThread = new Evaluator(this);
  mainThread->start((void (*)(vmkit::Thread*))mainRStart);
}

void Reactor::mainRStart(Evaluator* eval) {
  bc_header header;
  Reactor* vm = eval->vm();
	RModule* module;

  vm->argumentsInfo.readArgs(vm);

	// TODO A function for all this mess !
	SymbolTable::initializeSymbols();
	ConstPool::initConsts();
	RAttributes::initAttributesList();

  vm->toplevel = Environment::makeTopLevel();

	if(!vm->argumentsInfo.skipBaseLibrary){
		rprintf("******* Load base library *******");
		module = vm->allocateModule();
		module->load("rlib/base/base.Rc");
		eval->execute(module, vm->toplevel);
		rprintf("******* End of base loading *******");
	} else
		rprintf("******* Skipping base library *******");

	module = vm->allocateModule();
	module->load(vm->argumentsInfo.fname);

	if(vm->argumentsInfo.precompile) {
		RJITCompiler *jit = NULL;
		RModule* mod = NULL;
		jit = static_cast<RJITCompiler*>(vm->compiler);

		for(uint64_t i = 0; i < vm->modules.size(); i++) {
			mod = vm->modules.get(i);
			for(uint64_t funID = 0; funID < mod->nbFuns; funID++) {
				jit->jitCompile(mod->funInfos + funID);
			}
		}
	}

	if(vm->argumentsInfo.chronometer) {
		// Start timer
		printf("Starting timer\n");
		gettimeofday(&(vm->tbegin), NULL);
	}

	eval->execute(module, vm->toplevel);

	if(vm->argumentsInfo.chronometer) {
		// End timer
		gettimeofday(&(vm->tend), NULL);
		printf("Timer stopped\n");
		// Compute execution time
		vm->texec = ((double) (1000 * (vm->tend.tv_sec - vm->tbegin.tv_sec) + ((vm->tend.tv_usec - vm->tbegin.tv_usec) / 1000))) / 1000.;
		std::cout << "Execution time : " << vm->texec << " s" << std::endl;
	}

  vm->exit();
}

RModule* Reactor::lookupModule(uint64_t moduleId) {
	RModule* cur;

	for(uint64_t i=0; i<modules.size(); i++)
		if((cur = modules.get(i))->moduleId == moduleId)
			return cur;

	return 0;
}
	
RModule* Reactor::allocateModule() {
	RModule* res = new RModule(this, __sync_fetch_and_add(&curModuleId, 1));
	modules.push(res);
	return res;
}
